import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.LinkedList;

/**
 * This class handles all communications with the server
 * It's run on its own Thread, and updates based on GameSettings.
 * @author drichmond3
 *
 */
public class DBComm implements Runnable{

	String url = "";
	Game game;
	LinkedList<Drawable> items;
	Socket socket;
	DataInputStream in;
	DataOutputStream out;
	String tempSet;
	String completeSet;
	double LP;
	float EP;
	int RP;
	int error;
	boolean setComplete;
	String overrideMessage;
	Point center;
	String chat;
	/**
	 * Initializes all instance variables
	 * @param game the Game object that this comm 
	 * will be sending and receiving data from
	 */
	public DBComm(Game game)
	{
		error = 0;
		overrideMessage = "";
		this.game = game;
		tempSet = "";
		completeSet = "";
		items = new LinkedList<Drawable>();
		this.LP = 100;
		this.EP = 100;
		this.RP = 100;
		chat = "";
		center = new Point(50,50);
	}
	/**
	 * Starts communication with the server.  This method loops until the game is over,
	 * and every time it loops it down information from the server and sends
	 * up the current user commands to the server.  Reads the info from the server by calling
	 * decode
	 */
	@Override
	public void run() {
		int i = 0;
		while(true)
		{
			try {
				socket = new Socket(GameSettings.getIp(),7777);
				in = new DataInputStream(socket.getInputStream());
				out = new DataOutputStream(socket.getOutputStream());
				
				while(true)
				{
					
						if(overrideMessage.length() > 0)
						{
							out.writeUTF(overrideMessage);
							overrideMessage = "";
						}
						if(setComplete == true)
						{
							out.writeUTF(completeSet + chat );
							chat = "";
							setComplete = false;
						}
					String str = in.readUTF();
					i++;
					if(i > 20)
					{
						try{
							if(in.available()>500)
							{	
								in.skipBytes(in.available());
								System.out.println("behind");
							}
						}
						catch(Exception e)
						{
						}
						i = 0;
					}
					game.clearAttack();
					game.addDrawableItems(decode(str));
					game.setCenter(center);
					try {
						Thread.sleep(GameSettings.getUpdateRate());
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		 catch (Exception e1) {
				System.out.println("connection error: " + error);
				error++;
				if(error> 20000)
				{
					game.sendFatalMsg("connection error");
				}
			}
		}
		
	}
	/**
	 * closes the input and output stream to ensure no data leakage.
	 */
	public void close()
	{
		try {
			in.close();
			out.close();
		} catch (Exception e) {
		}
	}
	/**
	 * takes in the string received from the server and changes it into drawable objects
	 * for the display to use.
	 * @param input string received from the server
	 * @return LinkedList of the drawable items created from the server input.
	 */
	public LinkedList<Drawable> decode(String input)
	{
		try{
		if(input != null)
		{
			if(input.contains("null") == false)
			{
				LinkedList<Drawable>list = new LinkedList<Drawable>();
				while(input.length() > 5 && input.contains("\n"))
				{
					String str = input.substring(0,input.indexOf("\n"));
					if(str.contains("Stat") == true)
					{
						
					}
					else if(str.contains("CHAT") || str.contains("Chat"))  //read and send the chat from
					{                                                      //from all players to game
						if(str.length() > 6)
						{
							String a = str.substring(6);
							game.receiveChat(a);
						}
						input = input.substring(input.indexOf("\n") + 1);
					}
					//this string will be the center of the character.  This is the position
					//the display will draw everything around, and make this position the center of 
					//the screen
					else if(str.contains("Center"))  
					{
						
						int x = Integer.parseInt("" + str.charAt(6)+ str.charAt(7) + str.charAt(8) +
								str.charAt(9) + str.charAt(10) + 
								str.charAt(11) + str.charAt(12));
						int y = Integer.parseInt("" + str.charAt(13)+ str.charAt(14) + 
								str.charAt(15) + str.charAt(16) + str.charAt(17) + 
								str.charAt(18) + str.charAt(19));
						center = new Point(x,y);
						input = input.substring(input.indexOf("\n")+1);
					}
					else if(str.length() == 26)
					{
						str.trim();
						int x = Integer.parseInt("" + str.charAt(0) + str.charAt(1) + 
								str.charAt(2) + str.charAt(3) + str.charAt(4) + 
								str.charAt(5) + str.charAt(6));
						int y = Integer.parseInt("" + str.charAt(7) + str.charAt(8) + 
								str.charAt(9) + str.charAt(10) + str.charAt(11) + 
								str.charAt(12) + str.charAt(13));
						int angleDeg = Integer.parseInt("" + str.charAt(14) + str.charAt(15) + 
								str.charAt(16) + str.charAt(17) + str.charAt(18) + 
								str.charAt(19) + str.charAt(20));
						double angleRad = Math.toRadians(angleDeg);
						int character = Integer.parseInt("" + str.charAt(21) + str.charAt(22));
						int type = Integer.parseInt("" + str.charAt(23) + str.charAt(24));
						int img = Integer.parseInt("" + str.charAt(25));
						list.add(new Drawable(new Point(x,y),GameSettings.getImageStorage().getImage(character, type, img),(float)angleRad));
						input = input.substring(input.indexOf("\n")+1);
					}
					//special method for displaying attacks like the kamehameha wave.
					//adds this wave to the attack list of game.  The extra component of the drawable item
					//represents the length of the wave.
					else if(str.startsWith("Wave: "))
					{
						str = str.substring(6);
						int x = Integer.parseInt("" + str.charAt(0) + str.charAt(1) + 
								str.charAt(2) + str.charAt(3) + str.charAt(4) + 
								str.charAt(5) + str.charAt(6));
						int y = Integer.parseInt("" + str.charAt(7) + str.charAt(8) + 
								str.charAt(9) + str.charAt(10) + str.charAt(11) + 
								str.charAt(12) + str.charAt(13));
						int angleDeg = Integer.parseInt("" + str.charAt(14) + str.charAt(15) + 
								str.charAt(16) + str.charAt(17) + str.charAt(18) + 
								str.charAt(19) + str.charAt(20));
						double angleRad = Math.toRadians(angleDeg);
						int character = Integer.parseInt("" + str.charAt(21) + str.charAt(22));
						int type = Integer.parseInt("" + str.charAt(23) + str.charAt(24));
						int img = Integer.parseInt("" + str.charAt(25));
						int length = Integer.parseInt("" + str.charAt(26) + str.charAt(27) + str.charAt(28) + str.charAt(29));
						//loop to create all the attack images needed to depict this attack
						game.addAttack(new Drawable(new Point(x,y),GameSettings.getImageStorage().getImage(character, type, img),(float)angleRad), length);
						input = input.substring(input.indexOf("\n")+1);
					}
					else if(str.startsWith("Blast: "))
					{
						str = str.substring(7);
						int x = Integer.parseInt("" + str.charAt(0) + str.charAt(1) + 
								str.charAt(2) + str.charAt(3) + str.charAt(4) + 
								str.charAt(5) + str.charAt(6));
						int y = Integer.parseInt("" + str.charAt(7) + str.charAt(8) + 
								str.charAt(9) + str.charAt(10) + str.charAt(11) + 
								str.charAt(12) + str.charAt(13));
						int angleDeg = Integer.parseInt("" + str.charAt(14) + str.charAt(15) + 
								str.charAt(16) + str.charAt(17) + str.charAt(18) + 
								str.charAt(19) + str.charAt(20));
						double angleRad = Math.toRadians(angleDeg);
						int character = Integer.parseInt("" + str.charAt(21) + str.charAt(22));
						int type = Integer.parseInt("" + str.charAt(23) + str.charAt(24));
						int img = Integer.parseInt("" + str.charAt(25));
						game.addAttack(new Drawable(new Point(x,y),GameSettings.getImageStorage().getImage(character, type, img),(float)angleRad), 1994);
						input = input.substring(input.indexOf("\n")+1);
					}
					else if(str.startsWith("DARK"))
					{
						input = input.substring(input.indexOf("\n")+1);
						game.setDark();
					}
					//this will send up the character number to the server, if the server
					//has sent a request for the user ID.
					else if(str.contains("Send ID"))
					{
						String num = "" + game.getCharacterNumber();
						if(num.length() == 1)
						{
							num = "0" + num;
						}
						System.out.println(str);
						overrideMessage = "Character Number: " + num + "\n";
						input = input.substring(input.indexOf("\n")+1);
					}
					else
					{
						if(input.contains("\n") && input.length() >5)
						{
							input = input.substring(input.indexOf("\n")+1);
						}
						else
						{
							input = "";
						}
					}
				}
				return list;
			}
		}
		else
		{
			return null;
		}
		}
		catch(Exception e)
		{
			System.out.println(e);
		}
		return null;
	}
	/**
	 * adds a chat message to the string to be sent to the server.  Puts
	 * the chat code in front of the chat message.
	 * @param str the chat message
	 */
	public void sendChat(String str)
	{
		chat = chat + "Chat: " + str + "\n";
	}
	/**
	 * sends an action type command to the server.  Such as telling the server
	 * that the character is attempting to move left.  The server will then move the 
	 * character accordingly.
	 * @param action the action the character is trying to perform.  Example: LEFT
	 */
	public void sendAction(String action)
	{
		String temp = "Action: ";
		temp = temp +  action;
		tempSet = tempSet + temp + "\n";
	}
	/**
	 * tells the class that on the next update cycle, it can send out this data to the
	 * server, because it is a complete set of everything that needs to be sent in
	 * this cycle.  Also says that anything written to the DBComm from now on will not affect
	 * the next being sent to the server, but the set after that.
	 */
	public void completeSet()
	{
		completeSet = tempSet;
		setComplete = true;
	}
	/**
	 * empties the contents of tempSet to prepare for a new set of information to be
	 * sent out.  Be sure to call completeSet() first if you want the data from the old set
	 * to be sent.
	 */
	public void newSet()
	{
		tempSet = "";
	}
	/**
	 * adds zeros in front of an input string(usually in number format) to ensure that it
	 * is the required length.  For instance, inputting in 12 for str and 4 for length
	 * would output  0012
	 * @param str the string to be padded.
	 * @param length the length of the desired string
	 * @return the newly zero padded string
	 */
	public String paddZeros(String str,int length)
	{
		String a = str + "";
		if(a.length() > length)
		{
			str = str.substring(0,length);
		}
		while(a.length() < length)
		{
			if(str.charAt(0) == '-')
			{
				String temp = "-0" + a.substring(1);
				a = temp;
			}
			else
			{
				a = "0" + a;
			}
		}
		return a;
	}
	public void setCharacterNumber(int i)
	{
		//overrideMessage = "Character Number: 0" + i + "\n";
	}
}
